MySQL建立MySQL Container 與 simple_bank database.
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=secret --network bank-network -d mysql:8
docker exec -it mysql mysql -u root -psecret -e "CREATE DATABASE simple_bank;"
Connect to MySQL and access simple_bankdatabase
docker exec -it mysql mysql -uroot -psecret simple_bank
Get current isolation level in MySQL
Repeatable Read**select @@transaction_isolation; 這個SQL指令時,所獲得的隔離級別是針對當前的console session Level**Repeatable Read**mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
select @@global.transaction_isolation 這個SQL指令,用於查詢MySQL伺服器的全局隔離級別設定所獲得的隔離級別**Repeatable Read**mysql> select @@global.transaction_isolation;
+--------------------------------+
| @@global.transaction_isolation |
+--------------------------------+
| REPEATABLE-READ |
+--------------------------------+
1 row in set (0.00 sec)
Change isolation level in MySQL (current session)
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
建立accounts、entries、transfers Table
CREATE TABLE `accounts` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`owner` VARCHAR(255) NOT NULL,
`balance` BIGINT NOT NULL,
`currency` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `entries` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`account_id` BIGINT NOT NULL,
`amount` BIGINT NOT NULL COMMENT 'can be negative or positive',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE `transfers` (
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`from_account_id` BIGINT NOT NULL,
`to_account_id` BIGINT NOT NULL,
`amount` BIGINT NOT NULL COMMENT 'must be positive',
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX `accounts_index_0` ON `accounts` (`owner`);
CREATE UNIQUE INDEX `accounts_index_1` ON `accounts` (`owner`, `currency`);
CREATE INDEX `entries_index_2` ON `entries` (`account_id`);
CREATE INDEX `transfers_index_3` ON `transfers` (`from_account_id`);
CREATE INDEX `transfers_index_4` ON `transfers` (`to_account_id`);
CREATE INDEX `transfers_index_5` ON `transfers` (`from_account_id`, `to_account_id`);
ALTER TABLE `entries` ADD FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`);
ALTER TABLE `transfers` ADD FOREIGN KEY (`from_account_id`) REFERENCES `accounts` (`id`);
ALTER TABLE `transfers` ADD FOREIGN KEY (`to_account_id`) REFERENCES `accounts` (`id`);
插入三筆Account的資訊進到accounts
INSERT INTO `accounts` (`owner`, `balance`, `currency`) VALUES
('one', 100, 'USD'),
('two', 100, 'USD'),
('three', 100, 'USD');
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 100 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.01 sec)
Read uncommitted isolation level in MySQL建立兩個Console,並設定兩個Session的Isolation Level為**Read Uncommitted**
-- Tx1:
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1 中對accounts table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 100 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
在Tx2 中對accounts table進行id=1的 query
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
這時候在Tx1 中對Account 1的balance減去10
-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
最後我們在Tx1 與 Tx2 再次對Account 1進行Query
Tx1 尚未committed 但是Tx2 已經能看到Account 1的更新了dirty-read 並且只會發生在read-uncommitted isolation level-- Tx1
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
Read committed isolation level in MySQL建立兩個Console,並設定兩個Session的Isolation Level為**Read Committed**
-- Tx1:
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1 中對accounts table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
在Tx2 中對accounts table進行id=1的 query
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
這時候在Tx1 中對Account 1的balance減去10
-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
我們在Tx2 再次對Account 1進行Query
Tx1 尚未committed ,所以Tx2 的Account 1 Query 依舊是90read-committed 可以避免dirty read
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
How about non-repeatable and phantom read?
non-repeatable :
-- Tx2:
mysql> select * from accounts where balance >= 90;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 90 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
-- Tx1:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
再回到Tx2 對Account 1 進行查詢,可以發現和之前所獲得Value(90)不相同,這就被稱為non-repeatable (請對照 “我們在Tx2 再次對Account 1進行Query”)
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
phantom read
同樣我們在Tx2 進行同一個Query取得balance 90以上的Accounts,可以發現相同的Query但是Return 的Record 數量不相同,這就被稱為phantom read
mysql> select * from accounts where balance >= 90;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
2 rows in set (0.00 sec)
所以read-committed 只能避免dirty read 無法避開non-repeatable 、phantom read
Repeatable read isolation level in MySQL建立兩個Console,並設定兩個Session的Isolation Level為**Repeatable**
-- Tx1:
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1 中對accounts table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
在Tx2 中對accounts table進行id=1的查詢與balance ≥80 的Query
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> select * from accounts where balance >= 80;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
這時候在Tx1 中對Account 1的balance減去10
read committed」中已經阻止了dirty read , 所以就不需要再驗證-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.02 sec)
我們在Tx2 再次對Account 1進行查詢,來確認是否可以獲得Tx1 所更新的balance
Tx1已經將其更改為70並且成功commit。repeatable-read isolation level 確保所有的讀取查詢都是repeatable,這意味著,它總是返回相同的結果,即使其他已提交的交易進行了更改 (避免non-repeatable)。phantom read)。-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> select * from accounts where balance >= 80;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 80 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
如果我們在Tx2 對Account 1 的balance那結果會是如何?
rollback這個交易,並嘗試進入最高隔離級別,看看這個問題是否可以被防止。mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
MySQL使用locking mechanism來避免**Serialization anomaly**
建立兩個Console,並設定兩個Session的Isolation Level為**Repeatable**
-- Tx1:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1 中對accounts table進行select query,並進行Sum 和Insert
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 260 |
+--------------+
1 row in set (0.00 sec)
mysql> insert into accounts (owner, balance, currency) values ('sum', 260, 'USD');
Query OK, 1 row affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
+----+-------+---------+----------+---------------------+
4 rows in set (0.00 sec)
在Tx2 中對accounts table進行Select Query
-- Tx2
mysql> select * from accounts;
會發現這個查詢被blocked了,它需要等待Tx1釋放lock後才能繼續。
--Tx1
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
--Tx2
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
+----+-------+---------+----------+---------------------+
4 rows in set (1.79 sec)
這時候可以再對Tx2 中對accounts table進行select query,並進行Sum 和Insert
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
+----+-------+---------+----------+---------------------+
4 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 520 |
+--------------+
1 row in set (0.00 sec)
mysql> insert into accounts (owner, balance, currency) values ('sum', 520, 'USD');
Query OK, 1 row affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
| 4 | sum | 260 | USD | 2023-08-23 12:36:59 |
| 5 | sum | 520 | USD | 2023-08-23 13:36:59 |
+----+-------+---------+----------+---------------------+
5 rows in set (0.01 sec)
There’s no duplicate sum records. So MySQL has also successfully prevented the serialization anomaly with its locking mechanism.
接下來我現在我想在這兩個交易中嘗試不同的查詢順序
-- Tx1:
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2020-09-06 15:09:38 |
| 2 | two | 100 | USD | 2020-09-06 15:09:38 |
| 3 | three | 100 | USD | 2020-09-06 15:09:38 |
| 4 | sum | 260 | USD | 2020-09-15 14:36:20 |
| 5 | sum | 520 | USD | 2020-09-15 14:39:21 |
+----+-------+---------+----------+---------------------+
5 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 1040 |
+--------------+
1 row in set (0.00 sec)
--Tx2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2020-09-06 15:09:38 |
| 2 | two | 100 | USD | 2020-09-06 15:09:38 |
| 3 | three | 100 | USD | 2020-09-06 15:09:38 |
| 4 | sum | 260 | USD | 2020-09-15 14:36:20 |
| 5 | sum | 520 | USD | 2020-09-15 14:39:21 |
+----+-------+---------+----------+---------------------+
5 rows in set (0.00 sec)
mysql> select sum(balance) from accounts;
+--------------+
| sum(balance) |
+--------------+
| 1040 |
+--------------+
1 row in set (0.00 sec)
這次兩個交易都有相同的總和,為1040美元, 讓我們在Tx1中插入一個帶有此總和的新帳戶
Tx2 持有一個share lock,阻止其他交易進行更新。-- Tx1:
mysql> insert into accounts (owner, balance, currency) values ('sum', 1040, 'USD');
_
現在如果我們嘗試在Tx2中插入一個新的總和帳戶
Tx1 和 Tx2 在互相等待-- Tx2:
mysql> insert into accounts (owner, balance, currency) values ('sum', 1040, 'USD');
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
所以在Tx1 進行Commit後就可以正常運作
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2020-09-06 15:09:38 |
| 2 | two | 100 | USD | 2020-09-06 15:09:38 |
| 3 | three | 100 | USD | 2020-09-06 15:09:38 |
| 4 | sum | 260 | USD | 2020-09-15 14:36:20 |
| 5 | sum | 520 | USD | 2020-09-15 14:39:21 |
| 6 | sum | 1040 | USD | 2020-09-15 14:41:26 |
+----+-------+---------+----------+---------------------+
6 rows in set (0.00 sec)
The database stays consistent with no serialization anomaly.
建立兩個Console,並設定兩個Session的Isolation Level為**Repeatable**
-- Tx1:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+
1 row in set (0.00 sec)
建立Transaction
-- Tx1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- Tx2
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
在Tx1 中對accounts table進行select query
-- Tx1
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.00 sec)
在Tx2 中對accounts table進行id=1的查詢
-- Tx2
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2020-09-06 15:09:38 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
這時候在Tx1 中對Account 1的balance減去10
blocked
Tx2中的Select查詢正在阻擋Tx1中的這個更新查詢。-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
lock wait timeout error
serializable isolation level時要確保實現transaction retry strategy 避免timeout
-- Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
再來Restart Tx1 並進行Select Query 和Account 1的Update
--Tx1
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
| 2 | two | 100 | USD | 2023-08-23 07:05:50 |
| 3 | three | 100 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
3 rows in set (0.01 sec)
mysql> update accounts set balance = balance - 10 where id = 1;
我們再回到Tx2 對Account 1 進行Update
DeadLock ,因為Tx2 也需要等待Tx1的Select Query Locklock wait timeout外,您還需要注意可能的DeadLock情況--Tx2
mysql> update accounts set balance = balance - 10 where id = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
--Tx1
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (48.33 sec)
Rows matched: 1 Changed: 1 Warnings: 0
讓我們對Tx1 和 Tx2 進行重啟,並對Account 1 進行Select
-- Tx1:
mysql> rollback;
Query OK, 0 rows affected (0.03 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
-- Tx2:
mysql> rollback;
Query OK, 0 rows affected (0.03 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 70 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
讓我們在Tx1 更新Account 1 的Balance ,並且在 Tx2 進行commit
-- Tx1:
mysql> update accounts set balance = balance - 10 where id = 1;
-- Tx2:
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
### After Commit
-- Tx1:
mysql> update accounts set balance = balance - 10 where id = 1;
Query OK, 1 row affected (5.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from accounts where id = 1;
+----+-------+---------+----------+---------------------+
| id | owner | balance | currency | created_at |
+----+-------+---------+----------+---------------------+
| 1 | one | 60 | USD | 2023-08-23 07:05:50 |
+----+-------+---------+----------+---------------------+
1 row in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)